#pragma once
#include <iostream>
#include <cstdlib>
#include <string>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "InetAddr.hpp"
#include "Logger.hpp"
#include "Comm.hpp"


const static int default_backlog = 5;

class TcpServer
{
    private:
    static void Handler(int fd,InetAddr& addr)
    {
        while(true)
        {
            //使用read读取，write写回
            char buffer[1024] = {0};
            int n = read(fd,buffer,sizeof(buffer) - 1);
            if(n < 0)
            {
                LOG(LogLevel::FATAL) << "read error";
                break;
            }
            else if(n == 0)
            {
                //读端默认是阻塞的，不阻塞返回0，说明写端关闭
                LOG(LogLevel::INFO) << "client quit:" << addr.ToString();
                break;
            }
            else
            {
                buffer[n] = 0;
                std::cout << "client say:" << buffer << std::endl;
                std::string echo = "server echo ";
                echo += buffer;
                write(fd,echo.c_str(),echo.size()); 
            }
        }
        close(fd);
    }

    class ThreadData
    {
        public:
        ThreadData(int fd,InetAddr& addr):_fd(fd),_addr(addr)
        {}

        int _fd;
        InetAddr _addr;
    };

    static void* Routine(void* arg)
    {
        //解除join状态，线程结束后自动释放资源
        pthread_detach(pthread_self());
        ThreadData* ptr = static_cast<ThreadData*>(arg);
        Handler(ptr->_fd,ptr->_addr);
        return 0;
    }
    public:
    TcpServer(uint16_t port):_port(port),_sockfd(-1)
    {}


    void Init()
    {
        _sockfd = socket(AF_INET,SOCK_STREAM,0);
        InetAddr local(_port);
        int ret = bind(_sockfd,(struct sockaddr*)&local.Addr(),local.Len());
        if(ret < 0)
        {
            LOG(LogLevel::FATAL) << "bind error";
            exit(BIND_ERROR);
        }

        LOG(LogLevel::INFO) << "bind success";

        if(listen(_sockfd,default_backlog) < 0)
        {
            LOG(LogLevel::FATAL) << "listen error";
            exit(LISTEN_ERROR);
        }

        LOG(LogLevel::INFO) << "listen success";
    }


    void Start()
    {
        //进入监听模式后，开始接收连接请求
        while(true)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int fd = accept(_sockfd,(struct sockaddr*)&client,&len);//accept的行为默认是阻塞的
            if(fd < 0)
            {
                LOG(LogLevel::FATAL) << "accept error";
                exit(ACCEPT_ERROR);
            }
            InetAddr _client(client);
            LOG(LogLevel::INFO) << "accept success:" << _client.ToString();

            //接受完信息后，转而调用更上层的函数进行信息处理与回复

            //1.单线程的
            // Handler(fd,_client);

            //2. 多进程的
            
            //考虑到父进程需要不断接收连接请求，子进程无法正常回收，所以有两种方法进行解决
            //signal(SIGCHLD,SIG_IGN),这样主动设置后，就可以是子进程资源被自动回收
            //利用孙子进程称为孤儿进程，自动被系统回收资源

            // pid_t pid = fork();
            // if(pid < 0)
            // {
            //     LOG(LogLevel::FATAL) << "fork error";
            //     exit(FORK_ERROR);
            // }
            // else if(pid == 0)
            // {
            //     //listen_sockfd是专门用于接收连接请求的，用于父进程，子进程不需要
            //     close(_sockfd);
            //     //子进程退出，孙子进程完成任务
            //     if(fork() > 0)
            //         exit(OK);
            //     Handler(fd,_client);
            //     exit(OK);//孙子进程执行完就正常退出
            // }
            // else
            // {
            //     //父进程，关闭用于通信的fd,防止fd泄漏
            //     //父进程不用于通信
            //     close(fd);
                
            //     //不需要等待
            //     // int ret = waitpid(pid,nullptr,0);
            //     //(void)ret;
            // }
            
            // 3. 多线程,共用同一张文件描述符表，不能随意close
            pthread_t tid;
            ThreadData* data = new ThreadData(fd,_client);
            int ret = pthread_create(&tid,nullptr,Routine,(void*)data);
            if(ret != 0)
            {
                LOG(LogLevel::FATAL) << "pthread_create error";
                exit(PTHREAD_CREATE_ERROR);
            }

            //上述多进程，多线程的做法：1. 创建时本身有消耗(来一个连接，才创建一个) 2. 进程线程数，没有上线，容易超负荷
            
            //4. 线程池:最佳实践
        } 
    }

    private:
    int _sockfd;
    uint16_t _port;
};


